package com.caishi.lkx.order.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.caishi.lkx.common.config.DiamondConfig;
import com.caishi.lkx.user.model.DistributionUser;
import com.caishi.lkx.user.service.IDistributionUserService;
import com.zzw.common.Fetch;
import com.zzw.common.Wrappers;
import com.zzw.common.entity.IBaseMapper;
import com.zzw.common.exception.BizException;
import com.zzw.common.exception.BizRuntimeException;
import com.zzw.common.schedule.KSchedule;
import com.zzw.common.schedule.KScheduleDelay;
import com.zzw.common.threadpool.SysThreadPool;
import com.zzw.common.utils.KZEngine;
import com.zzw.common.utils.TimeUtil;
import com.zzw.common.utils.UUIDUtil;
import com.caishi.lkx.order.OrderResultCode;
import com.caishi.lkx.order.config.AssetsRouter;
import com.caishi.lkx.order.ienum.status.AssetsStatus;
import com.caishi.lkx.order.ienum.status.OrderStatus;
import com.caishi.lkx.order.ienum.type.OrderType;
import com.caishi.lkx.order.ienum.type.PaymentType;
import com.caishi.lkx.order.mapper.OrderMapper;
import com.caishi.lkx.order.model.OrderDetailModel;
import com.caishi.lkx.order.model.OrderModel;
import com.caishi.lkx.order.model.PaymentModel;
import com.caishi.lkx.order.model.UserAssetsDataModel;
import com.caishi.lkx.order.service.IOrderDetailService;
import com.caishi.lkx.order.service.IOrderService;
import com.caishi.lkx.order.service.IPaymentService;
import com.caishi.lkx.order.service.IUserAssetsDataService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

/**
 * @author by keray
 * date:2021/7/14 4:25 下午
 */
@Service("orderService")
@Slf4j
public class OrderServiceImpl implements IOrderService {

    @Resource
    private OrderMapper orderMapper;

    @Resource
    private IOrderDetailService orderDetailService;

    @Resource
    private DiamondConfig diamondConfig;

    @Resource
    private IUserAssetsDataService userAssetsDataService;

    @Resource
    private IPaymentService paymentService;
    @Resource
    private IDistributionUserService distributionUserService;


    @Override
    public IBaseMapper<OrderModel, String> getMapper() {
        return orderMapper;
    }

    @Override
    public String generateOrderSn(@NotNull OrderModel orderModel) {
        if (orderModel.getCreatedTime() == null) {
            orderModel.setCreatedTime(LocalDateTime.now());
        }
        String uid = UUIDUtil.generateUUIDByTimestamp();
        return TimeUtil.DATE_TIME_FORMATTER_SN.format(orderModel.getCreatedTime()) + uid.substring(uid.length() - 15);
    }

    @Override
    public @NotNull OrderModel previewOrder(String createdBy, String userId, OrderType type, List<OrderDetailModel> detailModels, String remark, PaymentType paymentType, String formula, Long price,Long groupId) {
        Assert.notEmpty(userId);
        Assert.notNull(type);
        Assert.notEmpty(detailModels);
        OrderModel order = OrderModel.builder()
                .orderDetail(detailModels)
                .orderStatus(OrderStatus.waitPay)
                .remark(remark)
                .userId(userId)
                .type(type)
                .groupId(groupId)
                .build();
        order.setCreatedBy(createdBy);
        if (type == OrderType.onlineOrder) {
            order.setExpiredDate(LocalDateTime.now().plusSeconds(diamondConfig.getOrderExpireSeconds()));
        }
        order.setId(this.generateOrderSn(order));
        if (price == null) {
            order.setPrice(priceFormulaCompute(computePrice(order), formula));
        } else {
            order.setPrice(price);
        }
        detailModels.forEach(detail -> {
            detail.setAssets(AssetsRouter.router.router(detail.getEntityType()).assetsDetail(detail.getEntityId()));
            detail.setOrderId(order.getId());
        });
        return order;
    }

    @Override
    public @NotNull OrderModel createOrder(String createdBy, String userId, OrderType type, List<OrderDetailModel> detailModels, String remark, PaymentType paymentType, String formula, Long price, String entityId,Long groupId) {
        OrderModel orderModel = previewOrder(createdBy, userId, type, detailModels, remark, paymentType, formula, price,groupId);
        orderModel.getOrderDetail().forEach(d -> {
            AssetsRouter.router.router(d.getEntityType()).createOrderDetailCheck(orderModel, d);
            Assert.isTrue(orderDetailService.insert(d) != null, "订单详情创建失败");
        });
        if (orderModel.getExpiredDate() != null) {
            owner(OrderServiceImpl.class).orderExpired(KZEngine.generateKz(orderModel.getExpiredDate()), orderModel.getId());
        }
        return insert(orderModel);
    }

    @Override
    public @NotNull OrderModel createOrder(String createdBy, String userId, OrderType type, List<OrderDetailModel> detailModels, String remark, PaymentType paymentType, String formula, String entityId,Long groupId) {
        return createOrder(createdBy, userId, type, detailModels, remark, paymentType, formula, null, entityId,groupId);
    }

    public @NotNull OrderModel createOrder(String createdBy, String userId, OrderType type, List<OrderDetailModel> detailModels, String remark, PaymentType paymentType, String formula,Long groupId) {
        return createOrder(createdBy, userId, type, detailModels, remark, paymentType, formula, null,groupId);
    }

    @Override
    public OrderModel createOrder(String createdBy, String userId, OrderType type, List<OrderDetailModel> detailModels, String remark, PaymentType paymentType, String formula, Long groupId, String recommendId) {
        OrderModel orderModel = previewOrder(createdBy, userId, type, detailModels, remark, paymentType, formula, null,groupId);
        // 传入的recommendId是否合法
        DistributionUser recommend = distributionUserService.getById(recommendId);
        if(recommend!=null){
            orderModel.setRecommendId(recommendId);
        }
        orderModel.setRecommendId(recommendId);
        orderModel.getOrderDetail().forEach(d -> {
            AssetsRouter.router.router(d.getEntityType()).createOrderDetailCheck(orderModel, d);
            Assert.isTrue(orderDetailService.insert(d) != null, "订单详情创建失败");
        });
        if (orderModel.getExpiredDate() != null) {
            owner(OrderServiceImpl.class).orderExpired(KZEngine.generateKz(orderModel.getExpiredDate()), orderModel.getId());
        }
        return insert(orderModel);
    }

    protected void orderPaySuccess(String orderId) {
        log.info("订单完成 orderId={}", orderId);
        if (orderMapper.update(null,
                Wrappers.<OrderModel>lambdaUpdate()
                        .set(OrderModel::getOrderStatus, OrderStatus.waitSend)
                        .eq(OrderModel::getId, orderId)
                        .and(w -> w.eq(OrderModel::getOrderStatus, OrderStatus.waitPay)
                                .or()
                                .eq(OrderModel::getOrderStatus, OrderStatus.waitSend)
                                .or()
                                .eq(OrderModel::getOrderStatus, OrderStatus.expired)
                        )
        ) != 1) {
            log.error("订单支付完成状态修改失败 orderId={}", orderId);
            throw new BizRuntimeException();
        }
    }

    @Override
    public OrderModel orderDetail(String orderId, boolean detailList, boolean product, boolean payDetail) {
        return orderDetail(getById(orderId), detailList, product, payDetail);
    }

    @Override
    public OrderModel orderDetail(OrderModel orderModel, boolean detailList, boolean product, boolean payDetail) {
        if (detailList) {
            orderModel.setOrderDetail(Fetch
                    .fetch(orderDetailService.selectOrderDetails(orderModel.getId()))
                    .then(list -> !product ? list :
                            list.parallelStream()
                                    .peek(detail -> detail.setAssets(AssetsRouter.router.router(detail.getEntityType()).assetsDetail(detail.getEntityId())))
                                    .collect(Collectors.toList())
                    )
                    .getData());
        }
        if (payDetail) {
            orderModel.setPaymentModel(paymentService.getPayModel(orderModel.getId()));
        }
        return orderModel;
    }

    @Override
    public void orderDelivery(String orderId) {
        log.info("order={} 订单发货到用户", orderId);
        OrderModel orderModel = orderDetail(orderId, true, false, false);
        if (CollUtil.isNotEmpty(orderModel.getOrderDetail())) {
            LocalDateTime now = LocalDateTime.now();
            orderModel.getOrderDetail().forEach(detail -> {
                UserAssetsDataModel assets = userAssetsDataService.issueAssets(
                        UserAssetsDataModel.builder()
                                .userId(orderModel.getUserId())
                                .groupId(orderModel.getGroupId())
                                .assetType(detail.getEntityType())
                                .assetMappingId(detail.getEntityId())
                                .count(detail.getTotalCount())
                                .status(AssetsStatus.effective)
                                .startTime(now)
                                .endTime(AssetsRouter.router.router(detail.getEntityType()).computeEndTime(orderModel, detail.getEntityId(), now))
                                .build(), orderModel, false);
                detail.setUserAssetId(assets.getId());
                orderDetailService.simpleUpdate(detail);
            });
            log.info("订单发货完成 orderId={}", orderId);
            if (orderMapper.update(null,
                    Wrappers.lambdaUpdate(new OrderModel())
                            .set(OrderModel::getOrderStatus, OrderStatus.waitSign)
                            .eq(OrderModel::getId, orderId)
                            .eq(OrderModel::getOrderStatus, OrderStatus.waitSend)
            ) == 1) {
                // 状态修改完成，添加定时任务自动完成订单
                owner(OrderServiceImpl.class).orderAutoSuccess(StrUtil.format("d{}", diamondConfig.getOrderDefaultFinishDay()), orderId);
            } else {
                log.error("订单发货完成，状态修改失败 orderId={}", orderId);
                throw new BizRuntimeException();
            }
        } else {
            log.warn("订单发货warn：订单详情为空，异常订单：data={}", orderModel);
            throw new BizRuntimeException();
        }
    }

    @Override
    public long computePrice(OrderModel orderModel) {
        long sum = 0;
        for (OrderDetailModel detailModel : orderModel.getOrderDetail()) {
            sum += orderDetailService.computePrice(orderModel, detailModel);
        }
        return sum;
    }


    @Override
    public long priceFormulaCompute(long original, String formula) {
        if (StrUtil.isBlank(formula)) {
            return original;
        }
        // 目前formula 就为折扣比例 0.8
        return (long) (original * Float.parseFloat(formula));
    }

    @Override
    public void userOrderStatusChange(String orderId, OrderStatus status, String userId) {
        Assert.isTrue(orderMapper.update(null,
                Wrappers.lambdaUpdate(new OrderModel())
                        .set(OrderModel::getOrderStatus, status)
                        .eq(OrderModel::getId, orderId)
                        .eq(StrUtil.isNotBlank(userId), OrderModel::getUserId, userId)
                        .eq(status == OrderStatus.cancel, OrderModel::getOrderStatus, OrderStatus.waitPay)
                        .and(status == OrderStatus.delete, w -> w
                                .ne(OrderModel::getOrderStatus, OrderStatus.applyRefund)
                                .ne(OrderModel::getOrderStatus, OrderStatus.refunding)
                                .ne(OrderModel::getOrderStatus, OrderStatus.waitSend))
        ) == 1);
    }

    @Override
    public void userOrderStatusChange(String orderId, OrderStatus status) {
        this.userOrderStatusChange(orderId, status, null);
    }

    @Override
    public String getOrderOnlinePayTitle(String id) {
        List<OrderDetailModel> detailModels = orderDetailService.selectOrderDetails(id);
        return detailModels.parallelStream()
                .map(detail -> AssetsRouter.router.router(detail.getEntityType()).orderOnlinePayTitle(getById(id), detail.getEntityId()))
                .collect(Collectors.joining("、"));
    }

    /**
     * <p>
     * <h3>作者 keray</h3>
     * <h3>时间： 2019/9/6 14:12</h3>
     * 让订单过期，过期订单不能支付
     * </p>
     *
     * @param id
     * @return <p> {@link } </p>
     * @throws
     */
    @KSchedule(beanName = "orderService", desc = "订单支付过期")
    public void orderExpired(@KScheduleDelay String kz, String id) {
        log.info("订单超时过期 orderId={}", id);
        orderMapper.update(
                OrderModel.builder()
                        .orderStatus(OrderStatus.expired)
                        .build(),
                Wrappers.lambdaUpdate(new OrderModel())
                        .eq(OrderModel::getId, id)
                        .eq(OrderModel::getOrderStatus, OrderStatus.waitPay)
        );
    }

    /**
     * <p>
     * <h3>作者 keray</h3>
     * <h3>时间： 2019/9/6 15:58</h3>
     * 订单自动完成
     * </p>
     *
     * @param kz
     * @param id
     * @return <p> {@link } </p>
     * @throws
     */
    @KSchedule(beanName = "orderService", desc = "订单自动完成（完成后能退款）", dynamicDelay = true)
    public void orderAutoSuccess(@KScheduleDelay String kz, String id) {
        log.info("开始订单到期自动完成 orderId={}", id);
        this.orderSuccess(id);
    }

    @Override
    public void orderSuccess(String orderId) {
        // 订单不在支付完成状态下不允许完结订单
        if (!paymentService.checkOrderPay(orderId)) {
            throw new BizRuntimeException(OrderResultCode.orderNotSuccess);
        }
        if (orderMapper.update(null,
                Wrappers.lambdaUpdate(new OrderModel())
                        .set(OrderModel::getOrderStatus, OrderStatus.success)
                        .eq(OrderModel::getId, orderId)
                        .eq(OrderModel::getOrderStatus, OrderStatus.waitSign)
        ) != 1) {
            log.warn("订单完成据修改失败 orderId={}", orderId);
        }
    }

    @Override
    public void applyRefund(String orderId, String currentUserId, boolean ownerCheck, boolean skipCheck) throws BizException {
        OrderModel orderModel = orderDetail(orderId, true, false, false);
        if (ownerCheck && !orderModel.getUserId().equals(currentUserId)) {
            throw new BizException(OrderResultCode.illegalOrderApplyRefund);
        }
        boolean status = orderModel.getOrderStatus() == OrderStatus.waitSend ||
                orderModel.getOrderStatus() == OrderStatus.waitSign ||
                orderModel.getOrderStatus() == OrderStatus.success;
        if (!status) {
            throw new BizRuntimeException();
        }
        if (!skipCheck) {
            // 校验订单内容是否允许退回
            if (orderModel.getOrderDetail().parallelStream()
                    .allMatch(detail -> AssetsRouter.router.router(detail.getEntityType())
                            .assetsAllowRefund(orderModel, detail, userAssetsDataService.getById(detail.getUserAssetId())))) {
                throw new BizException(OrderResultCode.userAssetsNoRefund);
            }
        }
        userOrderStatusChange(orderId, OrderStatus.applyRefund);
    }

    @Override
    public void orderConfirmRefund(String orderId, Long refundAmount) {
        OrderModel orderModel = orderDetail(orderId, true, false, false);
        if (orderModel.getOrderStatus() != OrderStatus.applyRefund && orderModel.getOrderStatus() != OrderStatus.refundFail) {
            throw new BizRuntimeException(OrderResultCode.orderNoInRefundStatus);
        }
        // 资产退款处理
        log.info("order={} 开始订单退货", orderId);
        PaymentModel paymentModel = paymentService.selectFirst(Wrappers.<PaymentModel>lambdaQuery().eq(PaymentModel::getOrderId, orderId));
        if (CollUtil.isNotEmpty(orderModel.getOrderDetail())) {
            if (!paymentModel.getPaymentType().equals(PaymentType.agentpay)) {
                orderModel.getOrderDetail()
                        .stream()
                        .forEach(detail -> {
                            AssetsRouter.router.router(detail.getEntityType()).assetsRefundCall(orderModel, userAssetsDataService.getById(detail.getUserAssetId()));
                        });
            }
            log.info("订单退货完成 orderId={}", orderId);
            userOrderStatusChange(orderId, OrderStatus.refundSuccess);
            // 订单退款流程完后开始支付单退款
            paymentService.payRefund(orderModel, refundAmount);
        } else {
            log.warn("订单退货warn：订单详情为空，异常订单：data={}", orderModel);
            throw new BizRuntimeException();
        }
    }

    @Override
    public IPage<OrderModel> search(Map<String, Object> param, Page<OrderModel> page, boolean total) {
        Future<Long> future = CompletableFuture.supplyAsync(() -> null);
        if (total) {
            future = SysThreadPool.submit(() -> orderMapper.searchCount(param));
        }
        List<OrderModel> data = orderMapper.search(page.offset(), page.getSize(), param,
                page.getOrders().stream().filter(OrderItem::isAsc).map(OrderItem::getColumn).collect(Collectors.toList()),
                page.getOrders().stream().filter(o -> !o.isAsc()).map(OrderItem::getColumn).collect(Collectors.toList())
        );
        try {
            Long count = future.get();
            return page.setRecords(data).setTotal(count == null ? page.getRecords().size() : count);
        } catch (InterruptedException | ExecutionException e) {
            throw new BizRuntimeException(e);
        }
    }

    @Override
    public Long orderAmount(Map<String, Object> param) {
        return orderMapper.countAmount(param);

    }

    @Override
    public long searchCount(Map<String, Object> param) {
        return orderMapper.searchCount(param);
    }

    public Boolean refuse(OrderModel order) {
        if (null == order) {
            return false;
        }
        switch (order.getOrderStatus()) {
            case waitSend:
            case waitSign:
            case success:
                return true;
            default:
                return false;

        }
    }

    @Override
    public IPage<OrderModel> agentOpenCourse(Page<OrderModel> page, List<String> userIds, String userId, OrderStatus orderStatus, LocalDateTime startTime, LocalDateTime endTime, OrderType type) {
        return this.page(page, com.zzw.common.Wrappers.<OrderModel>lambdaQuery()
                .eq(null != type, OrderModel::getType, type)
                .eq(null != orderStatus, OrderModel::getOrderStatus, orderStatus)
                .eq(StrUtil.isNotBlank(userId), OrderModel::getCreatedBy, userId)
                .in(CollUtil.isNotEmpty(userIds), OrderModel::getCreatedBy, userIds)
                .between(null != startTime && null != endTime, OrderModel::getCreatedTime, startTime, endTime)
                .orderByDesc(OrderModel::getCreatedTime)
        );
    }

    @Override
    public Long agentMoney(List<String> userIds, String userId, OrderStatus orderStatus, LocalDateTime startTime, LocalDateTime endTime, OrderType type) {
        return this.selectFirst(com.zzw.common.Wrappers.query(new OrderModel())
                .select("ifnull(sum(price),0) as money")
                .eq(null != type, "type", type)
                .eq(null != orderStatus, "order_status", orderStatus)
                .between(null != startTime && null != endTime, "created_time", startTime, endTime)
                .eq(StrUtil.isNotBlank(userId), "created_by", userId)
                .in(CollUtil.isNotEmpty(userIds), "created_by", userIds)).getMoney();
    }

    @Override
    public IPage<OrderModel> openCourseList(Page<OrderModel> page,Map<String, Object> param) {
        return this.orderMapper.openCourseList(page,param);
    }

    @Override
    public IPage<OrderModel> openCodeList(Page<OrderModel> page, Map<String, Object> param) {
        return this.orderMapper.openCodeList(page,param);
    }

    @Override
    public List<Map> priceCountLine(Map<String, Object> param) {
        return orderMapper.priceCountLine(param);
    }
}
